#include "wifi.h"

// HTTP服务器句柄
static httpd_handle_t server = NULL;
// DNS服务器UDP控制块
static struct udp_pcb *dns_pcb = NULL;

// 日志标签
static const char *TAG = "WiFi";

// 事件组用于Wi-Fi连接状态管理
EventGroupHandle_t wifi_event_group = NULL;
// 定义Wi-Fi连接成功事件位
const int WIFI_CONNECTED_BIT = BIT0;

// 配置页面HTML内容
static const char *CONFIG_HTML = \
    "<html><meta charset='UTF-8'>" \
    "<head><title>ESP32 WiFi配置</title>" \
    "<meta name='viewport' content='width=device-width, initial-scale=1'>" \
    "<style>" \
    "body { font-family: Arial; margin: 40px }" \
    "input { margin: 10px 0; padding: 10px; width: 90% }" \
    "button { padding: 10px 20px; background: #4CAF50; color: white; border: none }" \
    "</style>" \
    "</head>" \
    "<body>" \
    "<h2>ESP32 WiFi配置</h2>" \
    "<form action='/connect' method='post'>" \
    "<input type='text' name='ssid' placeholder='WiFi名称(SSID)' required><br>" \
    "<input type='password' name='password' placeholder='WiFi密码' required><br>" \
    "<button type='submit'>连接</button>" \
    "</form>" \
    "</body>" \
    "</html>";

// 保存WiFi配置到NVS(非易失性存储)
static void save_wifi_config(const char *ssid, const char *password)
{
    nvs_handle_t nvs_handle; // NVS操作句柄
    esp_err_t err;           // 错误码

    // 打开NVS存储区域，可读写模式
    err = nvs_open("storage", NVS_READWRITE, &nvs_handle);
    if (err != ESP_OK)
    {
        ESP_LOGE(TAG, "打开NVS失败: %s", esp_err_to_name(err));
        return;
    }

    // 保存SSID到NVS
    err = nvs_set_str(nvs_handle, "ssid", ssid);
    if (err != ESP_OK)
    {
        ESP_LOGE(TAG, "保存SSID失败: %s", esp_err_to_name(err));
    }

    // 保存密码到NVS
    err = nvs_set_str(nvs_handle, "password", password);
    if (err != ESP_OK)
    {
        ESP_LOGE(TAG, "保存密码失败: %s", esp_err_to_name(err));
    }

    // 提交更改到NVS
    err = nvs_commit(nvs_handle);
    if (err != ESP_OK)
    {
        ESP_LOGE(TAG, "提交NVS更改失败: %s", esp_err_to_name(err));
    }

    // 关闭NVS句柄
    nvs_close(nvs_handle);
    ESP_LOGI(TAG, "WiFi配置已保存到NVS");
}

// 连接到指定WiFi网络
static void connect_to_wifi(const char *ssid, const char *password)
{
    wifi_config_t wifi_config = {0}; // 初始化WiFi配置结构体

    // 复制SSID到配置结构体，确保不超过最大长度
    strncpy((char *)wifi_config.sta.ssid, ssid, sizeof(wifi_config.sta.ssid));
    // 复制密码到配置结构体，确保不超过最大长度
    strncpy((char *)wifi_config.sta.password, password, sizeof(wifi_config.sta.password));

    // 设置STA模式的WiFi配置
    esp_wifi_set_config(WIFI_IF_STA, &wifi_config);
    // 开始连接WiFi
    esp_wifi_connect();
    ESP_LOGI(TAG, "正在尝试连接WiFi: %s", ssid);
}

// 处理WiFi配置表单的POST请求
static esp_err_t connect_post_handler(httpd_req_t *req)
{
    char buf[100];                   // 用于存储请求数据的缓冲区
    int ret, remaining = req->content_len; // 读取状态和剩余数据长度

    // 接收POST请求数据
    if ((ret = httpd_req_recv(req, buf, MIN(remaining, sizeof(buf)))) <= 0)
    {
        return ESP_FAIL;
    }

    // 确保字符串以null结尾
    buf[ret] = '\0';
    ESP_LOGI(TAG, "收到配置数据: %s", buf);

    // 查找SSID和密码在请求数据中的位置
    char *ssid_start = strstr(buf, "ssid=");
    char *password_start = strstr(buf, "password=");

    // 如果找到SSID和密码
    if (ssid_start && password_start)
    {
        // 移动指针到SSID实际值的开始位置
        ssid_start += 5;
        // 查找SSID的结束位置(&符号)
        char *ssid_end = strchr(ssid_start, '&');
        if (ssid_end)
            *ssid_end = '\0'; // 在&符号处截断字符串

        // 移动指针到密码实际值的开始位置
        password_start += 9;
        // 查找密码的结束位置(&符号)
        char *password_end = strchr(password_start, '&');
        if (password_end)
            *password_end = '\0'; // 在&符号处截断字符串

        ESP_LOGI(TAG, "WIFI账号: %s", ssid_start);
        ESP_LOGI(TAG, "WIFI密码: %s", password_start);

        // 保存WiFi配置
        save_wifi_config(ssid_start, password_start);
        // 连接到配置的WiFi
        connect_to_wifi(ssid_start, password_start);
        
        // 设置HTTP响应类型为HTML
        httpd_resp_set_type(req, "text/html");
        // 发送连接中提示页面
        return httpd_resp_send(req, "<html><meta charset='UTF-8'><body><h2>配置已接收，正在尝试连接WiFi...</h2><p>连接结果将在设备连接成功后显示</p></body></html>", -1);
    }
    else
    {
        // 设置HTTP响应类型为HTML
        httpd_resp_set_type(req, "text/html");
        // 发送配置解析失败提示
        return httpd_resp_send(req, "<html><meta charset='UTF-8'><body><h2>配置解析失败!</h2></body></html>", -1);
    }
}

// 处理根URL的GET请求，返回WiFi配置页面
static esp_err_t root_get_handler(httpd_req_t *req)
{
    // 发送配置页面HTML内容
    return httpd_resp_send(req, CONFIG_HTML, HTTPD_RESP_USE_STRLEN);
}

// 处理Android设备的门户检测请求，重定向到配置页面
static esp_err_t generate_204_handler(httpd_req_t *req)
{
    // 设置HTTP重定向状态码
    httpd_resp_set_status(req, "302 Found");
    // 设置重定向目标为根页面
    httpd_resp_set_hdr(req, "Location", "/");
    // 发送空响应体
    return httpd_resp_send(req, NULL, 0);
}

// 处理iOS设备的门户检测请求，重定向到配置页面
static esp_err_t hotspot_detect_handler(httpd_req_t *req)
{
    // 设置HTTP重定向状态码
    httpd_resp_set_status(req, "302 Found");
    // 设置重定向目标为根页面
    httpd_resp_set_hdr(req, "Location", "/");
    // 发送空响应体
    return httpd_resp_send(req, NULL, 0);
}

// 处理Microsoft设备的门户检测请求，重定向到配置页面
static esp_err_t connectivity_check_handler(httpd_req_t *req)
{
    // 设置HTTP重定向状态码
    httpd_resp_set_status(req, "302 Found");
    // 设置重定向目标为根页面
    httpd_resp_set_hdr(req, "Location", "/");
    // 发送空响应体
    return httpd_resp_send(req, NULL, 0);
}

// 处理Apple设备的另一种门户检测请求，重定向到配置页面
static esp_err_t apple_captive_handler(httpd_req_t *req)
{
    // 设置HTTP重定向状态码
    httpd_resp_set_status(req, "302 Found");
    // 设置重定向目标为根页面
    httpd_resp_set_hdr(req, "Location", "/");
    // 发送空响应体
    return httpd_resp_send(req, NULL, 0);
}

// 根URL处理配置
static const httpd_uri_t root = {
    .uri = "/",                 // URI路径
    .method = HTTP_GET,         // HTTP方法
    .handler = root_get_handler,// 处理函数
    .user_ctx = NULL            // 用户上下文数据
};

// WiFi连接请求处理配置
static const httpd_uri_t connect = {
    .uri = "/connect",                  // URI路径
    .method = HTTP_POST,                 // HTTP方法
    .handler = connect_post_handler,     // 处理函数
    .user_ctx = NULL                     // 用户上下文数据
};

// Android门户检测请求处理配置
static const httpd_uri_t generate_204 = {
    .uri = "/generate_204",             // URI路径
    .method = HTTP_GET,                  // HTTP方法
    .handler = generate_204_handler,     // 处理函数
    .user_ctx = NULL                     // 用户上下文数据
};

// iOS门户检测请求处理配置
static const httpd_uri_t hotspot_detect = {
    .uri = "/hotspot-detect.html",      // URI路径
    .method = HTTP_GET,                  // HTTP方法
    .handler = hotspot_detect_handler,   // 处理函数
    .user_ctx = NULL                     // 用户上下文数据
};

// Microsoft门户检测请求处理配置
static const httpd_uri_t connectivity_check = {
    .uri = "/connectivity-check.html",  // URI路径
    .method = HTTP_GET,                  // HTTP方法
    .handler = connectivity_check_handler,// 处理函数
    .user_ctx = NULL                     // 用户上下文数据
};

// Apple另一种门户检测请求处理配置
static const httpd_uri_t apple_captive = {
    .uri = "/apple-touch-icon.html",    // URI路径
    .method = HTTP_GET,                  // HTTP方法
    .handler = apple_captive_handler,    // 处理函数
    .user_ctx = NULL                     // 用户上下文数据
};

// WiFi事件处理函数
static void wifi_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data)
{
    // 处理接入点启动事件
    if (event_id == WIFI_EVENT_AP_START)
    {
        ESP_LOGI(TAG, "WiFi AP启动成功");
    }
    // 处理设备连接到接入点事件
    else if (event_id == WIFI_EVENT_AP_STACONNECTED)
    {
        wifi_event_ap_staconnected_t *event = (wifi_event_ap_staconnected_t *)event_data;
        ESP_LOGI(TAG, "设备" MACSTR "连接到AP", MAC2STR(event->mac));
    }
    // 处理设备从接入点断开事件
    else if (event_id == WIFI_EVENT_AP_STADISCONNECTED)
    {
        wifi_event_ap_stadisconnected_t *event = (wifi_event_ap_stadisconnected_t *)event_data;
        ESP_LOGI(TAG, "设备" MACSTR "从AP断开", MAC2STR(event->mac));
    }
    // 处理ESP32连接到WiFi路由器事件
    else if (event_id == WIFI_EVENT_STA_CONNECTED)
    {
        ESP_LOGI(TAG, "已连接到WiFi路由器");
    }
    // 处理ESP32从WiFi路由器断开事件
    else if (event_id == WIFI_EVENT_STA_DISCONNECTED)
    {
        wifi_event_sta_disconnected_t *event = (wifi_event_sta_disconnected_t *)event_data;
        ESP_LOGI(TAG, "从WiFi断开，原因: %d", event->reason);
    }
}

// IP地址事件处理函数
static void ip_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data)
{
    // 处理STA模式获取IP地址事件
    if (event_id == IP_EVENT_STA_GOT_IP)
    {
        ip_event_got_ip_t *event = (ip_event_got_ip_t *)event_data;
        ESP_LOGI(TAG, "STA模式获取IP地址: " IPSTR, IP2STR(&event->ip_info.ip));
        
        // 设置WiFi连接成功事件位
        xEventGroupSetBits(wifi_event_group, WIFI_CONNECTED_BIT);
    }
}

// 从NVS加载保存的WiFi配置
static bool load_wifi_config(char *ssid, char *password, size_t max_len)
{
    nvs_handle_t nvs_handle; // NVS操作句柄
    esp_err_t err;           // 错误码
    size_t required_size;    // 所需缓冲区大小

    // 打开NVS存储区域，只读模式
    err = nvs_open("storage", NVS_READONLY, &nvs_handle);
    if (err != ESP_OK)
    {
        ESP_LOGI(TAG, "没有找到保存的WiFi配置");
        return false;
    }

    // 获取SSID所需的缓冲区大小
    err = nvs_get_str(nvs_handle, "ssid", NULL, &required_size);
    if (err == ESP_OK && required_size <= max_len)
    {
        // 实际读取SSID值
        nvs_get_str(nvs_handle, "ssid", ssid, &required_size);
        ESP_LOGI(TAG, "加载SSID: %s", ssid);
    }
    else
    {
        nvs_close(nvs_handle);
        return false;
    }

    // 获取密码所需的缓冲区大小
    err = nvs_get_str(nvs_handle, "password", NULL, &required_size);
    if (err == ESP_OK && required_size <= max_len)
    {
        // 实际读取密码值
        nvs_get_str(nvs_handle, "password", password, &required_size);
        ESP_LOGI(TAG, "加载密码: %s", password);
    }
    else
    {
        nvs_close(nvs_handle);
        return false;
    }

    // 关闭NVS句柄
    nvs_close(nvs_handle);
    return true;
}

// DNS查询处理函数，将所有DNS查询解析到ESP32的IP地址
static void dns_query_handler(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port)
{
    uint8_t *data = (uint8_t *)p->payload; // DNS查询数据

    // 只处理标准查询，忽略反向查询等
    if ((data[2] & 0x80) != 0)
    {
        pbuf_free(p);
        return;
    }

    // 创建DNS响应数据包
    struct pbuf *response = pbuf_alloc(PBUF_TRANSPORT, p->tot_len + 16, PBUF_RAM);
    if (!response)
    {
        pbuf_free(p);
        return;
    }

    // 复制查询数据到响应
    pbuf_copy(response, p);
    uint8_t *resp_data = (uint8_t *)response->payload;

    // 设置响应标志位
    resp_data[2] = 0x81; // 响应标志
    resp_data[3] = 0x80; // 无错误

    // 设置回答数为1
    resp_data[6] = 0x00;
    resp_data[7] = 0x01;

    // 跳过查询部分，定位到添加回答的位置
    uint16_t query_len = DNS_HEADER_SIZE;
    while (query_len < response->len && resp_data[query_len] != 0)
    {
        query_len += resp_data[query_len] + 1;
    }
    query_len += 5; // 跳过末尾的0和查询类型、类

    // 添加DNS回答记录
    resp_data[query_len++] = 0xC0; // 指针到域名
    resp_data[query_len++] = 0x0C; // 指向查询的域名位置

    resp_data[query_len++] = 0x00; // 类型A
    resp_data[query_len++] = 0x01;

    resp_data[query_len++] = 0x00; // 类IN
    resp_data[query_len++] = 0x01;

    resp_data[query_len++] = 0x00; // TTL 300秒
    resp_data[query_len++] = 0x00;
    resp_data[query_len++] = 0x01;
    resp_data[query_len++] = 0x2C;

    resp_data[query_len++] = 0x00; // 数据长度4字节
    resp_data[query_len++] = 0x04;

    resp_data[query_len++] = 192; // IP地址192.168.4.1
    resp_data[query_len++] = 168;
    resp_data[query_len++] = 4;
    resp_data[query_len++] = 1;

    // 发送DNS响应
    udp_sendto(dns_pcb, response, addr, port);

    // 释放内存
    pbuf_free(p);
    pbuf_free(response);
}

// 启动DNS服务器，解析所有域名到ESP32自身IP
static void start_dns_server(void)
{
    // 创建UDP控制块
    dns_pcb = udp_new();
    if (!dns_pcb)
    {
        ESP_LOGE(TAG, "创建DNS PCB失败");
        return;
    }

    // 绑定DNS服务端口
    if (udp_bind(dns_pcb, IP_ADDR_ANY, DNS_PORT) != ERR_OK)
    {
        ESP_LOGE(TAG, "绑定DNS端口失败");
        udp_remove(dns_pcb);
        dns_pcb = NULL;
        return;
    }

    // 注册DNS查询处理回调函数
    udp_recv(dns_pcb, dns_query_handler, NULL);
    ESP_LOGI(TAG, "DNS服务器启动成功，端口: %d", DNS_PORT);
}

// 通配符URI处理器，捕获所有未匹配的请求并重定向到配置页面
static esp_err_t wildcard_handler(httpd_req_t *req)
{
    // 检查是否已经是配置页面
    if (strcmp(req->uri, "/") == 0)
    {
        return root_get_handler(req);
    }

    // 重定向到配置页面
    httpd_resp_set_status(req, "302 Found");
    httpd_resp_set_hdr(req, "Location", "/");
    return httpd_resp_send(req, NULL, 0);
}

// 通配符URI处理配置
static const httpd_uri_t wildcard = {
    .uri = "*",                 // 通配符URI匹配所有未注册路径
    .method = HTTP_GET,         // HTTP方法
    .handler = wildcard_handler,// 处理函数
    .user_ctx = NULL            // 用户上下文数据
};

// 启动HTTP服务器并注册URI处理程序
static void start_webserver(void)
{
    // 使用默认HTTP服务器配置
    httpd_config_t config = HTTPD_DEFAULT_CONFIG();

    // 启动HTTP服务器
    if (httpd_start(&server, &config) == ESP_OK)
    {
        // 注册URI处理程序
        httpd_register_uri_handler(server, &root);                   // 根页面
        httpd_register_uri_handler(server, &connect);                // WiFi连接请求
        httpd_register_uri_handler(server, &generate_204);           // Android门户检测
        httpd_register_uri_handler(server, &hotspot_detect);         // iOS门户检测
        httpd_register_uri_handler(server, &connectivity_check);     // Microsoft门户检测
        httpd_register_uri_handler(server, &apple_captive);          // Apple另一种门户检测
        httpd_register_uri_handler(server, &wildcard);               // 通配符处理器
        ESP_LOGI(TAG, "HTTP服务器启动成功");
    }
    else
    {
        ESP_LOGE(TAG, "HTTP服务器启动失败");
    }
}

// 停止配置服务器
void stop_config_server(void)
{
    if (server) {
        httpd_stop(server);  // 停止HTTP服务器
        server = NULL;
        ESP_LOGI(TAG, "配置服务器已停止");
    }
    
    if (dns_pcb) {
        udp_remove(dns_pcb);  // 停止DNS服务器
        dns_pcb = NULL;
        ESP_LOGI(TAG, "DNS服务器已停止");
    }
}

// WiFi初始化函数，配置AP+STA模式并启动相关服务
void wifi_init(void)
{
    // 创建事件组
    wifi_event_group = xEventGroupCreate();
    
    // 初始化NVS
    esp_err_t ret = nvs_flash_init();
    if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND)
    {
        // 如果NVS已满或版本不匹配，擦除后重新初始化
        ESP_ERROR_CHECK(nvs_flash_erase());
        ret = nvs_flash_init();
    }
    
    // 初始化网络接口
    esp_netif_init();
    // 创建默认事件循环
    esp_event_loop_create_default();

    // 初始化WiFi配置
    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    esp_wifi_init(&cfg);

    // 创建默认WiFi接入点接口
    esp_netif_t *ap_netif = esp_netif_create_default_wifi_ap();
    // 创建默认WiFi站点接口
    esp_netif_t *sta_netif = esp_netif_create_default_wifi_sta();
    
    // 设置STA接口的主机名
    if (sta_netif != NULL)
    {
        esp_netif_set_hostname(sta_netif, ESP32_HOSTNAME);
    }

    // 注册WiFi事件处理器
    esp_event_handler_instance_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &wifi_event_handler, NULL, NULL);
    // 注册IP事件处理器
    esp_event_handler_instance_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &ip_event_handler, NULL, NULL);

    // 设置WiFi为AP+STA双模
    esp_wifi_set_mode(WIFI_MODE_APSTA);

    // 配置AP模式参数
    wifi_config_t wifi_config = {
        .ap = {
            .ssid = WIFI_SSID,              // AP名称
            .password = WIFI_PASS,          // AP密码
            .ssid_len = strlen(WIFI_SSID),  // SSID长度
            .channel = 1,                   // 信道
            .authmode = WIFI_AUTH_OPEN,     // 认证模式(开放)
            .max_connection = 4,            // 最大连接数
            .pmf_cfg = {
                .required = false,          // 禁用PMF保护
            },
        },
    };

    // 设置AP模式的固定IP地址
    esp_netif_ip_info_t ip_info;
    IP4_ADDR(&ip_info.ip, 192, 168, 4, 1);     // 设置IP地址
    IP4_ADDR(&ip_info.gw, 192, 168, 4, 1);     // 设置网关地址
    IP4_ADDR(&ip_info.netmask, 255, 255, 255, 0); // 设置子网掩码
    esp_netif_dhcps_stop(ap_netif);            // 停止DHCP服务器
    esp_netif_set_ip_info(ap_netif, &ip_info); // 设置静态IP
    esp_netif_dhcps_start(ap_netif);           // 重新启动DHCP服务器

    // 应用AP配置
    esp_wifi_set_config(WIFI_IF_AP, &wifi_config);

    // 检查是否有保存的WiFi配置
    char saved_ssid[32] = {0};
    char saved_password[64] = {0};
    if (load_wifi_config(saved_ssid, saved_password, sizeof(saved_ssid)))
    {
        ESP_LOGI(TAG, "找到保存的WiFi配置，尝试连接");
        connect_to_wifi(saved_ssid, saved_password);
    }

    // 启动WiFi
    esp_wifi_start();

    ESP_LOGI(TAG, "WiFi AP初始化完成，SSID: %s", WIFI_SSID);

    // 启动HTTP服务器和DNS服务器
    start_webserver();
    start_dns_server();
}